iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 10
0
自我挑戰組

C# 從入門到WebApi系列 第 10

[Day10] 多執行緒與同步問題

  • 分享至 

  • xImage
  •  

前言

在有關Web方面我們常常會使用多執行緒以及非同布方法
儘管已經接觸一段時間
對於race conditon的處理還不是很嫻熟
本篇只會講解比較基本的多緒概念
會著重在如何使用多緒
建議若是想要深入程式設計這塊
可以去研讀下作業系統

有關執行緒的大小事

執行緒是作業系統分配CPU的最小單位
他們共享同個程式的一些共有變數
(上面是OS的敘述)
在C#內依行為來區分執行緒可分為兩種:
前景執行緒和背景執行緒
兩者的主要區別是:當所有的前景執行緒停止時,應用程式就會結束,並且停止所有背景執行緒。
若只是停止背景執行緒,則不會造成應用程式結束。

** 簡單來說執行緒就是一個附生在主程式的程式 他會做你指派給他的工作**

建立執行緒

請引用命名空間System.Threading

using System.Threading

來看個範例

using System;
using System.Threading;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            Thread thread = new Thread(Run);//指派工作給執行緒
            thread.Start();//使用thread.Start(); 來開始執行緒工作 
            while (true)
            {
                Console.WriteLine("防守!防守!");
                Thread.Sleep(500);
                //使用Tread.Sleep()讓執行緒暫時停止工作 單位為毫秒
            }
        }

        static void Run()
        {
            while (true)
            {
                Console.WriteLine("執行緒大軍進攻拉");
                Thread.Sleep(1000);
            }
        }
    }
}

https://ithelp.ithome.com.tw/upload/images/20200910/20109549Zq3BhKzp3V.png

Race Condition(競爭條件)

假設我們有一個訂票網站
使用者A跟使用者B今天都想要訂同一場演唱會的門票
但是座位只剩下一個
A跟B剛好在同一時間打開網站
A看見還有位置就下了訂單
B也看到還有位置也下了單
但是系統沒有針對這個位置做處理
最後A只能坐在B的大腿上
主辦單位賺兩分錢 可喜可賀(X

上面的問題是設計購物網站常出現的問題

我們再來看看貼近我們這章要講的執行緒的例子吧
我們有一個共享變數v
初值為5

public static int v = 5;

執行緒A做的事是把v+1

static void RunA()
{
      v = v + 1;
}

執行緒B做的事是把v-1

static void RunB()
{
      v = v - 1;
}
void Main(){
Thread A = new Thread(RunA);
Thread B = new Thread(RunB);
A.Start();
B.Start();
Console.WriteLine(v);
}

上面v上看下看左看右看 都是5
實際上v 會有可能有4 5 6
3種答案

狀況1

v = v + 1; // v = 6
v = v - 1; // v = 5
v = 5

狀況2

其實上面的狀況能拆成4個步驟

step action
1 Thread a 執行 v + 1
2 Thread a 將v + 1 的值 assign給 v
3 Thread b 執行 v - 1
4 Thread b 將v + - 的值 assign給 v

如果今天按照1324的步驟執行 那麼v就會變成 4
按照3142的步驟 v 會等於6

這種情況我們就稱為競爭條件(race condtion)
或是同步問題

解決同步問題

可以使用lock
我們先宣告一個共用的物件object(因為int型態不能鎖)

public static object lockObj;

改寫下RunA跟RunB

static void RunA()
{
    lock(lockObj){
        v = v + 1;
    }
}
static void RunB()
{
    lock(lockObj){
        v = v - 1;
    }
}

這樣子在被lock{}中的程式碼就成為中華民國不可分割的領土了不能分割的運算式了
優先搶到lockObj的人能夠先執行運算式
直到執行完會釋放lockObj
因為lockObj只有一份
所以同一時間只為有一個人搶到

我們稱沒有同步問題的咚咚為執行緒安全(Thread safety)

lock 的缺點

容易因為忘記釋放或是設計不量導致死結(DeadLock)( http://ccckmit.wikidot.com/cs-deadlock )

實作執行緒安全的資料結構

我們在[Day6] 今晚我想來點基礎的資料結構中有提到
3種資料結構 Stack Queue 跟 Dictionary
事實上它們都不是執行緒安全的結構
也就是今天兩個執行緒興高采烈要到同一個Queue裡面拿東西時
A先把東西拿走了
B沒有發現A把東西拿走了
所以B也傻傻地去Queue裡面拿東西
好死不死那是最後一個
B就會出例外

因此在需要考慮執行緒安全的結構可以使用
ConccurentQueue ConcurrentDictionary 跟 ConcurrentStack
用法跟原本的很像
只是取出變成要try
詳閱Dequeue

非同步方法

我覺得這部分官方的教學做的很棒
有圖片輔助學習
所以麻煩大家花個5分鐘看看官方文件
使用 async 和 await 進行非同步程式設計

閒聊

今天也不知不覺第10天了越來越沒有動力
這邊一些基礎跟進階的語法都講得差不多了
接下來才是我原本想起頭的地方
原本只是因為怕30天塞不滿
結果根本是想太多了RRR


上一篇
[Day9] Lambda 表示式(匿名委派) 與 LINQ
下一篇
[Day11] 論物件導向三本柱之一(封裝)
系列文
C# 從入門到WebApi30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言